From 5a84d1bfb796e3ff87d26036a8556d0ef22b3c54 Mon Sep 17 00:00:00 2001 From: "ach61@labyrinth.cl.cam.ac.uk" Date: Thu, 5 Feb 2004 20:27:42 +0000 Subject: [PATCH] bitkeeper revision 1.698.1.1 (4022a73eOGiwh6ZU9-EU4LZoLcVnuQ) pervasive debugger --- .rootkeys | 7 + README.CD | 10 +- docs/pdb.txt | 215 +++++++ tools/nsplitd/Makefile | 11 + tools/nsplitd/nsplitd.c | 686 +++++++++++++++++++++ xen/arch/i386/pdb-stub.c | 831 ++++++++++++++++++++++++++ xen/arch/i386/setup.c | 2 + xen/arch/i386/traps.c | 65 +- xen/common/debug-linux.c | 82 +++ xen/common/debug-linux.h | 45 ++ xen/common/debug.c | 183 +++++- xen/common/kernel.c | 3 + xen/drivers/char/xen_serial.c | 113 +++- xen/include/asm-i386/pdb.h | 41 ++ xen/include/hypervisor-ifs/dom0_ops.h | 8 +- xen/include/xeno/sched.h | 2 +- 16 files changed, 2276 insertions(+), 28 deletions(-) create mode 100644 docs/pdb.txt create mode 100644 tools/nsplitd/Makefile create mode 100644 tools/nsplitd/nsplitd.c create mode 100644 xen/arch/i386/pdb-stub.c create mode 100644 xen/common/debug-linux.c create mode 100644 xen/common/debug-linux.h create mode 100644 xen/include/asm-i386/pdb.h diff --git a/.rootkeys b/.rootkeys index 65a76aac8b..703a59d1e7 100644 --- a/.rootkeys +++ b/.rootkeys @@ -12,6 +12,7 @@ 3f9e7d60PWZJeVh5xdnk0nLUdxlqEA docs/eps/xenlogo.eps 3f9e7d63lTwQbp2fnx7yY93epWS-eQ docs/figs/dummy 3f9e7d564bWFB-Czjv1qdmE6o0GqNg docs/interface.tex +4022a73cgxX1ryj1HgS-IwwB6NUi2A docs/pdb.txt 3f9e7d58t7N6hjjBMxSn-NMxBphchA docs/style.tex 3f9e7d5bz8BwYkNuwyiPVu7JJG441A docs/xenstyle.cls 3f815144d1vI2777JI-dO4wk49Iw7g extras/mini-os/Makefile @@ -73,6 +74,8 @@ 3f13d81e6Z6806ihYYUw8GVKNkYnuw tools/misc/xen_nat_enable.README 3f1668d4F29Jsw0aC0bJEIkOBiagiQ tools/misc/xen_read_console.c 3f87ba90EUVPQLVOlFG0sW89BCwouQ tools/misc/xen_refresh_dev.c +4022a73cEKvrYe_DVZW2JlAxobg9wg tools/nsplitd/Makefile +4022a73cKms4Oq030x2JBzUB426lAQ tools/nsplitd/nsplitd.c 3fbca441SjQr8vJwTQIgH1laysaWog tools/xc/Makefile 3fbba6dbDfYvJSsw9500b4SZyUhxjQ tools/xc/lib/Makefile 3fbba6dc1uU7U3IFeF6A-XEOYF2MkQ tools/xc/lib/rpm.spec @@ -118,6 +121,7 @@ 3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/i386/pci-i386.h 3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/i386/pci-irq.c 3ddb79bcZ_2FxINljqNSkqa17ISyJw xen/arch/i386/pci-pc.c +4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/i386/pdb-stub.c 3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/i386/process.c 3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/i386/rwlock.c 3ddb79bcrD6Z_rUvSDgrvjyb4846Eg xen/arch/i386/setup.c @@ -133,6 +137,8 @@ 3ddb79bddEYJbcURvqqcx99Yl2iAhQ xen/common/block.c 3ddb79bdrqnW93GR9gZk1OJe1qK-iQ xen/common/brlock.c 3fb10d07GscSWPKxBqpvNfU-dYfa0g xen/common/console.c +4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c +4022a73c_KPZ1VEbYOrpAhQffd01kA xen/common/debug-linux.h 3fa152581E5KhrAtqZef2Sr5NKTz4w xen/common/debug.c 3ddb79bdLX_P6iB7ILiblRLWvebapg xen/common/dom0_ops.c 3e6377e4i0c9GtKN65e99OtRbw3AZw xen/common/dom_mem_ops.c @@ -394,6 +400,7 @@ 3ddb79c3xjYnrv5t3VqYlR4tNEOl4Q xen/include/asm-i386/page.h 3e450943kzme29HPCtq5HNOVQkddfw xen/include/asm-i386/param.h 3ddb79c3ysKUbxZuwKBRK3WXU2TlEg xen/include/asm-i386/pci.h +4022a73diKn2Ax4-R4gzk59lm1YdDg xen/include/asm-i386/pdb.h 3ddb79c3nm2zdzeO6Mj8g7ex3txgGw xen/include/asm-i386/pgalloc.h 3ddb79c2QF5-pZGzuX4QukPCDAl59A xen/include/asm-i386/processor.h 3ddb79c3mbqEM7QQr3zVq7NiBNhouA xen/include/asm-i386/ptrace.h diff --git a/README.CD b/README.CD index a69b165ea3..f4478b853a 100644 --- a/README.CD +++ b/README.CD @@ -240,10 +240,16 @@ that may be able to help diagnose problems: ifname=dummy Don't use any network interface. - ser_baud=xxx Enable serial I/O and set the baud rate. + ser_baud=xxx Enable serial I/O and set the baud rate (COM1) dom0_mem=xxx Set the initial amount of memory for domain0. - + + pdb=xxx Enable the pervasive debugger. See docs/pdb.txt + xxx defines how the gdb stub will communicate: + com1 use com1 + com1H use com1 (with high bit set) + com2 use on com2 + com2H use com2 (with high bit set) It's probably a good idea to join the Xen developer's mailing list on Sourceforge: http://lists.sourceforge.net/lists/listinfo/xen-devel diff --git a/docs/pdb.txt b/docs/pdb.txt new file mode 100644 index 0000000000..aab57c5a42 --- /dev/null +++ b/docs/pdb.txt @@ -0,0 +1,215 @@ +Pervasive Debugging +=================== + +040205 Alex Ho (alex.ho@cl.cam.ac.uk) + +Introduction +------------ + +The pervasive debugging project is leveraging Xen to +debug distributed systems. We have added a gdb stub +to Xen to allow for remote debugging of both Xen and +guest operating systems. More information about the +pervasive debugger is available at: http://www.cl.cam.ac.uk/netos/pdb + + +Implementation +-------------- + +The gdb stub communicates with gdb running over a serial line. +The main entry point is pdb_handle_exception() which is invoked +from: pdb_key_pressed() ('D' on the console) + do_int3_exception() (interrupt 3: breakpoint exception) + do_debug() (interrupt 1: debug exception) + +This accepts characters from the serial port and passes gdb +commands to pdb_process_command() which implements the gdb stub +interface. This file draws heavily from the kgdb project and +sample gdbstub provided with gdb. + +The stub can examine registers, single step and continue, and +read and write memory (in Xen, a domain, or a Linux process' +address space). The debugger does not currently trace the +current process, so all bets are off if context switch occurs +in the domain. + + +Setup +----- + + +-------+ telnet +-----------+ serial +-------+ + | GDB |--------| nsplitd |--------| Xen | + +-------+ +-----------+ +-------+ + +To run pdb, Xen must be appropriately configured and +a suitable serial interface attached to the target machine. +GDB and nsplitd can run on the same machine. + +Xen Configuration + + Add the "pdb=xxx" option to your Xen boot command line + where xxx is one of the following values: + com1 gdb stub should communicate on com1 + com1H gdb stub should communicate on com1 (with high bit set) + com2 gdb stub should communicate on com2 + com2H gdb stub should communicate on com2 (with high bit set) + + Symbolic debugging infomration is quite helpful too: + xeno.bk/xen/arch/i386/Rules.mk + add -g to CFLAGS to compile Xen with symbols + xeno.bk/xenolinux-2.4.24-sparse/arch/xeno/Makefile + add -g to CFLAGS to compile XenoLinux with symbols + + You may also want to consider dedicating a register to the + frame pointer (disable the -fomit-frame-pointer compile flag). + + When booting Xen and domain 0, look for the console text + "Initializing pervasive debugger (PDB)" just before DOM0 starts up. + +Serial Port Configuration + + pdb expects to communicate with gdb using the serial port. Since + this port is often shared with the machine's console output, pdb can + discriminate its communication by setting the high bit of each char. + + A new tool has been added to the source tree which splits + the serial output from a remote machine into two streams: + one stream (without the high bit) is the console and + one stream (with the high bit stripped) is the pdb communication. + + See: xeno.bk/tools/nsplitd + + Note: nsplitd was originally written for the Nemesis project + at Cambridge. + + Usage: + %telnet + This is the console of the remote machine. You will probably + want to set telnet in char mode (or create a .telnetrc file). + and are for your instance of nsplitd. + You should be able to press 'h' to display a list of keyboard + handlers. In particular, you should see: + key 'D' (ascii '44') => enter pervasive debugger + + After nsplitd accepts a connection on , it starts listening + on port . Characters sent to the will have the + high bit set and vice versa for characters received. + + Note: if you are not using a serial console and can dedicate your + serial line to pdb messages, then edit pdb_put_char and pdb_get_char + in pdb-stub.c to remove the high bit tests. + +GDB 6.0 + pdb has been tested with gdb 6.0. It should also work with + earlier versions. + + +Usage +----- + +1. Boot Xen and XenoLinux +2. Interrupt Xen by pressing 'D' at the console + You should see the console message: + pdb_handle_exception [0x88][0xfc5c9d88] + At this point Xen is waiting for gdb commands on the serial line. +3. Attach with gdb + (gdb) file xeno.bk/xen/xen + Reading symbols from xeno.bk/xen/xen...done. + (gdb) target remote : /* contact nsplitd */ + Remote debugging using serial.srg:12131 + continue_cpu_idle_loop () at current.h:10 + warning: shared library handler failed to enable breakpoint + (gdb) break __enter_scheduler + Breakpoint 1 at 0xfc510a94: file schedule.c, line 330. + (gdb) cont + Continuing. + + Program received signal SIGTRAP, Trace/breakpoint trap. + __enter_scheduler () at schedule.c:330 + (gdb) step + (gdb) step + (gdb) print next /* the variable prev has been optimized away! */ + $1 = (struct task_struct *) 0x0 + (gdb) delete + Delete all breakpoints? (y or n) y +4. You can add additional symbols to gdb + (gdb) add-sym xenolinux-2.4.24/vmlinux + add symbol table from file "xenolinux-2.4.24/vmlinux" at + (y or n) y + Reading symbols from xenolinux-2.4.24/vmlinux...done. + (gdb) x/s cpu_vendor_names[0] + 0xc01530d2 : "Intel" + (gdb) break free_uid + Breakpoint 2 at 0xc0012250 + (gdb) cont + Continuing. /* run a command in domain 0 */ + + Program received signal SIGTRAP, Trace/breakpoint trap. + free_uid (up=0xbffff738) at user.c:77 + + (gdb) print *up + $2 = {__count = {counter = 0}, processes = {counter = 135190120}, files = { + counter = 0}, next = 0x395, pprev = 0xbffff878, uid = 134701041} + (gdb) finish + Run till exit from #0 free_uid (up=0xbffff738) at user.c:77 + + Program received signal SIGTRAP, Trace/breakpoint trap. + release_task (p=0xc2da0000) at exit.c:51 + (gdb) print *p +$3 = {state = 4, flags = 4, sigpending = 0, addr_limit = {seg = 3221225472}, + exec_domain = 0xc016a040, need_resched = 0, ptrace = 0, lock_depth = -1, + counter = 1, nice = 0, policy = 0, mm = 0x0, processor = 0, + cpus_runnable = 1, cpus_allowed = 4294967295, run_list = {next = 0x0, + prev = 0x0}, sleep_time = 18995, next_task = 0xc017c000, + prev_task = 0xc2f94000, active_mm = 0x0, local_pages = {next = 0xc2da0054, + prev = 0xc2da0054}, allocation_order = 0, nr_local_pages = 0, + binfmt = 0xc016c6a0, exit_code = 0, exit_signal = 17, pdeath_signal = 0, + personality = 0, did_exec = -1, task_dumpable = 1, pid = 917, pgrp = 914, + tty_old_pgrp = 0, session = 914, tgid = 917, leader = 0, + p_opptr = 0xc2f94000, p_pptr = 0xc2f94000, p_cptr = 0x0, p_ysptr = 0x0, + p_osptr = 0x0, thread_group = {next = 0xc2da00a8, prev = 0xc2da00a8}, + pidhash_next = 0x0, pidhash_pprev = 0xc01900b8, wait_chldexit = { + lock = , task_list = {next = 0xc2da00b8, + prev = 0xc2da00b8}}, vfork_done = 0x0, rt_priority = 0, + it_real_value = 0, it_prof_value = 0, it_virt_value = 0, it_real_incr = 0, + it_prof_incr = 0, it_virt_incr = 0, real_timer = {list = {next = 0x0, + prev = 0x0}, expires = 18950, data = 3269066752, + function = 0xc000ce30 }, times = {tms_utime = 0, + tms_stime = 0, tms_cutime = 0, tms_cstime = 0}, start_time = 18989, + per_cpu_utime = {1}, per_cpu_stime = {310}, min_flt = 13, maj_flt = 104, + nswap = 0, cmin_flt = 0, cmaj_flt = 0, cnswap = 0, swappable = -1, uid = 0, + euid = 0, suid = 0, fsuid = 0, gid = 0, egid = 0, sgid = 0, fsgid = 0, + ngroups = 7, groups = {0, 1, 2, 3, 4, 6, 10, 0 }, + cap_effective = 4294967039, cap_inheritable = 0, cap_permitted = 4294967039, + keep_capabilities = 0, user = 0xc016b18c, rlim = {{rlim_cur = 4294967295, + rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}, { + rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 8388608, + rlim_max = 4294967295}, {rlim_cur = 0, rlim_max = 4294967295}, { + rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 512, + rlim_max = 512}, {rlim_cur = 1024, rlim_max = 1024}, { + rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 4294967295, + rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}}, + used_math = 0, comm = "id\000h\000og\000\000\000\000\000\000\000\000", + link_count = 0, total_link_count = 1, tty = 0xc3ed1000, locks = 0, + semundo = 0x0, semsleeping = 0x0, thread = {esp0 = 3269074944, + eip = 3221249046, esp = 3269074792, fs = 0, gs = 0, io_pl = 3, debugreg = { + 0, 0, 0, 0, 0, 0, 0, 0}, cr2 = 0, trap_no = 0, error_code = 0, i387 = { + fsave = {cwd = 2098047, swd = 125632512, twd = 1073944696, fip = 2091, + fcs = -1073745032, foo = 2099, fos = 8064, st_space = { + 0 }, status = 0}, fxsave = {cwd = 895, swd = 32, + twd = 0, fop = 1917, fip = 1073944696, fcs = 2091, foo = -1073745032, + fos = 2099, mxcsr = 8064, reserved = 0, st_space = { + 0 , 1449431204, -1774489361, 16383, 0, 1, + -1891252224, 16404, 0}, xmm_space = {0 }, + padding = {0 }}, soft = {cwd = 2098047, + swd = 125632512, twd = 1073944696, fip = 2091, fcs = -1073745032, + foo = 2099, fos = 8064, st_space = {0 }, + ftop = 0 '\0', changed = 0 '\0', lookahead = 0 '\0', + no_update = 0 '\0', rm = 0 '\0', alimit = 0 '\0', info = 0x0, + entry_eip = 0}}, vm86_info = 0x0, screen_bitmap = 0, v86flags = 0, + v86mask = 0, saved_esp0 = 0}, fs = 0x0, files = 0x0, namespace = 0x0, + sigmask_lock = , sig = 0x0, blocked = {sig = {0, 0}}, + pending = {head = 0x0, tail = 0xc2da04f8, signal = {sig = {0, 0}}}, + sas_ss_sp = 0, sas_ss_size = 0, notifier = 0, notifier_data = 0x0, + notifier_mask = 0x0, parent_exec_id = 7, self_exec_id = 8, + alloc_lock = , journal_info = 0x0} diff --git a/tools/nsplitd/Makefile b/tools/nsplitd/Makefile new file mode 100644 index 0000000000..4f59075eb2 --- /dev/null +++ b/tools/nsplitd/Makefile @@ -0,0 +1,11 @@ +ROOT = .. + +CFILES = $(wildcard *.c) + +INSTALL_BINS := nsplitd + +include $(ROOT)/mk/rules.mk + +nsplitd: $(OBJS) + $(CC) $(CFLAGS) -o nsplitd $(OBJS) + diff --git a/tools/nsplitd/nsplitd.c b/tools/nsplitd/nsplitd.c new file mode 100644 index 0000000000..48fbd65103 --- /dev/null +++ b/tools/nsplitd/nsplitd.c @@ -0,0 +1,686 @@ +/* + * nsplitd.c + * --------- + * + * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ + * + * Copyright (c) 1995, University of Cambridge Computer Laboratory, + * Copyright (c) 1995, Richard Black, All Rights Reserved. + * + * + * A complete re-implementation of DME's nsplitd for use from inetd + * + */ + +/* The basic stream comes in (via inetd) and we then conenct to + * somewhere else providing a loop-through service, except we offer + * two other ports for connection - one of which gets a second channel + * using the top bit to distinguish, and the other is a master control + * port (normally used for gdb) which gets complete exclusive access + * for its duration. + * + * Originally designed for multiplexing a xwcons/telnet with a gdb + * post-mortem debugging session. + * + * Here is a picture: + * + * port0 (from inetd) + * 8-bit connection / + * made by us <----> nsplitd <-----gdbport (default port0+2) + * to host:port/tcp |\ + * | port1 (default port0+1) + * \ + * control (default port0+3) + * + * If port1 is explicitly disabled (through a command-line option) then + * port0 becomes 8-bit clean. + */ + +/* + * N.B.: We do NOT support 8 bit stdin/stdout usage on a + * /dev/... because to do that right involves much messing with ioctl + * and TIOC... etc. If you want to do that sort of thing then the + * right way to do it is to chain this onto wconsd (which does know + * about and understand all the ioctl and TIOC grief). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif + +#define DB(x) /* ((x), fflush(stderr)) */ + +extern char *optarg; + +extern int optind, opterr, optopt; + +static char *prog_name; + +static void usage(void) +{ + fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n", + prog_name); + fprintf(stderr, "usage: %s [-h][-g]" + "[-c][-8] host:service\n", + prog_name); + exit(1); +} + +static void fault(char *format, ...) +{ + va_list ap; + char logbuf[1024]; + + va_start(ap, format); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, format, ap); + fflush(stderr); + va_end(ap); + + /* XXX This is a bit dubious, but there is no vsyslog */ + va_start(ap, format); + vsprintf(logbuf, format, ap); + syslog(LOG_ERR, logbuf); + va_end(ap); + exit(1); +} + +static int getservice(char *name, unsigned short *port) +{ + struct servent *se; + + if (!name) return -1; + + if (isdigit(name[0])) + *port = atoi(name); + else + { + if (!(se = getservbyname(name, "tcp"))) + return -1; + *port = ntohs(se->s_port); + } + return 0; +} + +/* + * connect_host: connect to ("name", "port") + */ +static int connect_host (char *name, unsigned int port) +{ + int fd; + struct hostent *hostent; + struct sockaddr_in sin; + int on; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + fault("socket"); + + if (!(hostent = gethostbyname(name))) + fault("gethostbyname: %s: %s\n", name, strerror(errno)); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr)); + + if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) + fault("connect: %s:%u: %s\n", name, port, strerror(errno)); + + on = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); + + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + + return fd; +} + +/* + * open a tcp socket and start listening for connections on it + */ +static int startlistening(unsigned short port) +{ + int fd, on; + struct sockaddr_in sin; + + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + fault("socket"); + + on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + sin.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, &sin, sizeof(sin)) < 0) + fault("bind: %u: %s\n", port, strerror(errno)); + + if (listen(fd, 1) < 0) + fault("listen: %s\n", strerror(errno)); + + return fd; +} + +static void noblock(int fd) +{ + int on=1; + + if (ioctl(fd, FIONBIO, &on) < 0) + fault("ioctl: FIONBIO: %s\n", strerror(errno)); +} + + +/* You might not believe this, but fd_sets don't have to be a 32-bit + * integer. In particular, in glibc2 it is an array of unsigned + * longs. Hence, this hacked up FD_SET_rjb() that works out if it + * would have been a nop. */ +#define FD_SET_rjb(fd, setp) \ +do { \ + if ((fd) != 32) \ + FD_SET((fd), (setp)); \ +} while(0) + +#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0) + +#define MAXSIZE 256 + +/* ----------------------------------------------------------------- + * The main bit of the algorithm. Note we use 32 to mean not connected + * because this gives us 1<<32 == 0. We could have done this one + * character at a time, but that would have been very inefficient and + * not the unix way. */ +static int debug; + +static void doit(int actl, int acto, int lish, int lisg, int lisc) +{ + int acth, actg, actc; + int gdbmode = FALSE; + char gibuf[MAXSIZE], oibuf[MAXSIZE]; + char libuf[MAXSIZE], lobuf[MAXSIZE]; + char hibuf[MAXSIZE], hobuf[MAXSIZE]; + char ctlbuf[MAXSIZE]; + fd_set rdfs, wrfs, exfs; + int gicc, oicc, licc, locc, hicc, hocc, ctlcc; + char *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr; + int rc, fromlen; + struct sockaddr_in from; + + gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0; + acth = actg = actc = 32; /* XXX yummy */ + + noblock(actl); + noblock(acto); + + for(;;) + { + FD_ZERO(&rdfs); + FD_ZERO(&wrfs); + FD_ZERO(&exfs); + + /* always take input from the control port (if it's connected) */ + FD_SET_rjb(actc, &rdfs); + + if (gdbmode) + { + if (oicc) + FD_SET_rjb(actg, &wrfs); + else + FD_SET_rjb(acto, &rdfs); + + if (gicc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(actg, &rdfs); + } + else + { + /* There is no such thing as oibuf because its been split into + * lobuf and hobuf + */ + if (locc || hocc) + { + if (locc) + FD_SET_rjb(actl, &wrfs); + if (hocc) + FD_SET_rjb(acth, &wrfs); + } + else + FD_SET_rjb(acto, &rdfs); + + if (licc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(actl, &rdfs); + + if (hicc) + FD_SET_rjb(acto, &wrfs); + else + FD_SET_rjb(acth, &rdfs); + } + + if (acth == 32 && lish>=0) FD_SET_rjb(lish, &rdfs); + if (actg == 32) FD_SET_rjb(lisg, &rdfs); + if (actc == 32) FD_SET_rjb(lisc, &rdfs); + + /* now make exfs the union of the read and write fd sets, plus + * "actl" */ + { + int i; + exfs = rdfs; + for(i=0; i<32; i++) /* XXX we only copy fd numbers up to 31 */ + if (FD_ISSET(i, &wrfs)) + FD_SET_rjb(i, &exfs); + FD_SET_rjb(actl, &exfs); + } + + /* XXX AND: can't print something of type fd_set as %x - it + * might be an array */ + DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n", + prog_name, rdfs, wrfs, exfs)); + + if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0) + fault("select: %s\n", strerror(errno)); + + DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n", + prog_name, rdfs, wrfs, exfs)); + + /* XXX it appears that a non-blocking socket may not show up + * correctly in exfs but instead goes readable with no data in + * it. Thus we check for zero and goto the appropriate close + * method. */ + + /* Deal with exceptions */ + if (FD_ISSET_rjb(actg, &exfs)) + { + exfs_actg: + close(actg); + gdbmode = FALSE; + oicc = 0; + oiptr = oibuf; + actg = 32; + continue; /* because assumptions changed */ + } + if (FD_ISSET_rjb(acth, &exfs)) + { + exfs_acth: + close(acth); + hicc = hocc = 0; + hiptr = hibuf; + hoptr = hibuf; + acth = 32; + continue; /* because assumptions changed */ + } + if (FD_ISSET_rjb(actl, &exfs) || + FD_ISSET_rjb(acto, &exfs)) + { + exfs_actl: + exfs_acto: + /* Thats all folks ... */ + break; + } + if (FD_ISSET_rjb(actc, &exfs)) + { + exfs_ctl: + close(actc); + actc = 32; + ctlcc = 0; + continue; + } + + /* Deal with reading */ + if (FD_ISSET_rjb(acto, &rdfs)) + { + if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0) + fault("read acto: %d: %s\n", oicc, strerror(errno)); + if (!oicc) goto exfs_acto; + + if (!gdbmode) + { + int t; + + assert((locc == 0) && (hocc == 0)); + loptr = lobuf; + hoptr = hobuf; + + if (lish>=0) { + for(t=0; t=0 && (FD_ISSET_rjb(lish, &rdfs))) + { + fromlen = sizeof(from); + if ((acth = accept(lish, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept: %m"); + acth = 32; + } + else + { + noblock(acth); + hicc = hocc = 0; + syslog(LOG_INFO, "highbit client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs))) + { + fromlen = sizeof(from); + if ((actg = accept(lisg, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept: %m"); + actg = 32; + } + else + { + noblock(actg); + gicc = 0; + gdbmode = TRUE; + syslog(LOG_INFO, "gdb client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs))) + { + fromlen = sizeof(from); + if ((actc = accept(lisc, &from, &fromlen)) < 0) + { + syslog(LOG_WARNING, "accept (ctl): %m"); + actc = 32; + } + else + { + noblock(actc); + syslog(LOG_INFO, "ctl client peer is %s:%u\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + } + } + + /* Back to top of loop */ + } + + /* We are bailing because one of the primary connections has gone + * away. We close these all explicitly here because that way the + * timeout on reusing the port numbers is smnaller. */ + + close(acth); + close(actg); + /* XXX AND: why are we closing all these "character counts" ?? */ + close(gicc); + close(oicc); + close(licc); + close(locc); + close(hicc); + close(hocc); +} + +/* + * ------------------------------------------------------------ + */ +int main(int argc, char **argv) +{ + /* In general, suffix "l" is low channel, "h" is high channel, "g" + * is gdb channel, "c" is control channel and "o" is output channel. + */ + struct sockaddr_in from; + int infd = 0, outfd; + unsigned short portl, porth, portg, portc, porto; + int on = 1, c; + char *outname, *outservice; + int fromlen; + int lish, lisg, lisc; +#if 0 + FILE *newerr; +#endif /* 0 */ + + prog_name = argv[0]; + + if (isatty(infd)) + usage(); + + /* Here, then not just a simple idiot. */ + + signal(SIGPIPE, SIG_IGN); + + openlog(prog_name, LOG_PID, LOG_DAEMON); + + fromlen = sizeof(from); + if (getsockname(infd, &from, &fromlen) < 0) + fault("getsockname: %s", strerror(errno)); + if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET)) + fault("not an inet socket (family=%d)\n", from.sin_family); + + portl = ntohs(from.sin_port); + porth = portl+1; + portg = porth+1; + portc = portg+1; + + fromlen = sizeof(from); + if (getpeername(infd, &from, &fromlen) < 0) + fault("getpeername: %s", strerror(errno)); + if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET)) + fault("not an inet socket (family=%d)\n", from.sin_family); + + syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl, + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + + if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + + /* from here on, we map stderr to output on the connection so we can + * report errors to the remote user. + */ +#if 0 + if (!(newerr = fdopen(infd, "w"))) + syslog(LOG_WARNING, "fdopen: %m"); + else + *stderr = *newerr; +#endif + + while((c = getopt(argc, argv, "d8h:g:c:")) != EOF) + { + switch(c) + { + case 'd': + debug++; + break; + + case 'h': + /* high bit port */ + if (getservice(optarg, &porth) < 0) + fault("getservice failed (high port '%s')\n", optarg); + break; + + case 'g': + /* gdb port */ + if (getservice(optarg, &portg) < 0) + fault("getservice failed (gdb port '%s')\n", optarg); + break; + + case 'c': + /* control port */ + if (getservice(optarg, &portc) < 0) + fault("getservice failed (control port '%s')\n", optarg); + break; + + case '8': + /* 8-bit clean; no high port */ + porth=0; + break; + + default: + fault("bad argument list!\n"); + } + } + + if (argc != optind + 1) + fault("unparsed arguments (%d!=%d)\n", argc, optind+1); + + outname = argv[optind]; + if (!(outservice = strchr(outname, ':'))) + fault("output arg '%s' doesn't contain ':'\n", outname); + *outservice++ = 0; + if (getservice(outservice, &porto) < 0) + fault("getservice failed (output port '%s')\n", outservice); + + /* Time to start the sockets */ + + if (porth) { + lish = startlistening(porth); + } else { + lish = -1; + } + lisg = startlistening(portg); + lisc = startlistening(portc); + + outfd = connect_host(outname, porto); + + doit(infd, outfd, lish, lisg, lisc); + + syslog(LOG_INFO, "terminating normally\n"); + + fclose(stderr); + + closelog(); + exit(0); +} + +/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */ diff --git a/xen/arch/i386/pdb-stub.c b/xen/arch/i386/pdb-stub.c new file mode 100644 index 0000000000..7734f2f649 --- /dev/null +++ b/xen/arch/i386/pdb-stub.c @@ -0,0 +1,831 @@ +#include +#include +#include +#include +#include +#include + +#undef DEBUG_TRACE +#ifdef DEBUG_TRACE +#define TRC(_x) _x +#else +#define TRC(_x) +#endif + +#define BUFMAX 400 + +#define PDB_DOMAIN_OFFSET 2 /* all domains are positive numbers */ + +static const char hexchars[]="0123456789abcdef"; + +int remote_debug; + +int pdb_foobar = 0x123456; /* testing */ +char *pdb_foobaz = "cambridge"; /* testing */ + +#define PDB_BUFMAX 1024 +static char pdb_in_buffer[PDB_BUFMAX]; +static char pdb_out_buffer[PDB_BUFMAX]; +static char pdb_buffer[PDB_BUFMAX]; +static int pdb_in_buffer_ptr; +static unsigned char pdb_in_checksum; +static unsigned char pdb_xmit_checksum; + +int pdb_ctrl_thread = -1; +int pdb_info_thread = -1; +int pdb_stepping = 0; + +int hex (char); +char *mem2hex (char *, char *, int); +char *hex2mem (char *, char *, int); +int hexToInt (char **ptr, int *intValue); + +void pdb_put_packet (unsigned char *buffer, int ack); +void pdb_put_char (u_char c); +u_char pdb_get_char (); + +static volatile int mem_err = 0; +void set_mem_err (void) /* NOT USED YET... */ +{ + mem_err = 1; +} + +/* These are separate functions so that they are so short and sweet + that the compiler won't save any registers (if there is a fault + to mem_fault, they won't get restored, so there better not be any + saved). */ +int +get_char (char *addr) +{ + return *addr; +} + +void +set_char (char *addr, int val) +{ + *addr = val; +} + +void +pdb_process_query (char *ptr) +{ + if (strcmp(ptr, "C") == 0) + { + /* empty string */ + } + else if (strcmp(ptr, "fThreadInfo") == 0) + { + struct task_struct *p = &idle0_task; + u_long flags; + int count = 0, buf_idx = 0; + + read_lock_irqsave (&tasklist_lock, flags); + + pdb_out_buffer[buf_idx++] = 'm'; + while ( (p = p->next_task) != &idle0_task ) + { + int domain = p->domain + PDB_DOMAIN_OFFSET; + + if (count > 0) + pdb_out_buffer[buf_idx++] = ','; + /* + if (domain < 0) + { pdb_out_buffer[buf_idx++] = '-'; domain = domain * -1; } + */ + if (domain > 15) + { + pdb_out_buffer[buf_idx++] = hexchars[domain >> 4]; + } + pdb_out_buffer[buf_idx++] = hexchars[domain % 16]; + count++; + } + pdb_out_buffer[buf_idx++] = 'l'; + pdb_out_buffer[buf_idx++] = 0; + + read_unlock_irqrestore(&tasklist_lock, flags); + } + else if (strcmp(ptr, "sThreadInfo") == 0) + { + } + else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0) + { + int thread = 0; + char *message = "whatever!"; + + ptr += 16; + if (hexToInt (&ptr, &thread)) + { + mem2hex ((char *)message, pdb_out_buffer, strlen(message) + 1); + } + } + else if (strcmp(ptr, "Offsets") == 0) + { + /* empty string */ + } + else if (strncmp(ptr, "Symbol", 6) == 0) + { + strcpy (pdb_out_buffer, "OK"); + } + else + { + printk("pdb_process_query: unknown query [%s]\n", ptr); + } +} + +int +pdb_process_command (char *ptr, struct pt_regs *regs) +{ + int sigval = 10; + int length; + unsigned long addr; + int ack = 1; /* wait for ack in pdb_put_packet */ + int go = 0; + + TRC(printk("pdb: [%s]\n", ptr)); + { + pdb_out_buffer[0] = 0; + + switch (*ptr++) + { + case '?': + pdb_out_buffer[0] = 'S'; + pdb_out_buffer[1] = hexchars[sigval >> 4]; + pdb_out_buffer[2] = hexchars[sigval % 16]; + pdb_out_buffer[3] = 0; + break; + case 'S': /* step with signal */ + case 's': /* step */ + regs->eflags |= 0x100; + pdb_stepping = 1; + return 1; + /* not reached */ + case 'C': /* continue with signal */ + case 'c': /* continue */ + regs->eflags &= ~0x100; + /* jump out before replying to gdb */ + return 1; + /* not reached */ + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + break; + case 'D': /* detach */ + return go; + /* not reached */ + case 'g': /* return the value of the CPU registers */ + { + int idx = 0; + mem2hex ((char *)®s->eax, &pdb_out_buffer[idx], sizeof(regs->eax)); + idx += sizeof(regs->eax) * 2; + mem2hex ((char *)®s->ecx, &pdb_out_buffer[idx], sizeof(regs->ecx)); + idx += sizeof(regs->ecx) * 2; + mem2hex ((char *)®s->edx, &pdb_out_buffer[idx], sizeof(regs->edx)); + idx += sizeof(regs->edx) * 2; + mem2hex ((char *)®s->ebx, &pdb_out_buffer[idx], sizeof(regs->ebx)); + idx += sizeof(regs->ebx) * 2; + mem2hex ((char *)®s->esp, &pdb_out_buffer[idx], sizeof(regs->esp)); + idx += sizeof(regs->esp) * 2; + mem2hex ((char *)®s->ebp, &pdb_out_buffer[idx], sizeof(regs->ebp)); + idx += sizeof(regs->ebp) * 2; + mem2hex ((char *)®s->esi, &pdb_out_buffer[idx], sizeof(regs->esi)); + idx += sizeof(regs->esi) * 2; + mem2hex ((char *)®s->edi, &pdb_out_buffer[idx], sizeof(regs->edi)); + idx += sizeof(regs->edi) * 2; + mem2hex ((char *)®s->eip, &pdb_out_buffer[idx], sizeof(regs->eip)); + idx += sizeof(regs->eip) * 2; + mem2hex ((char *)®s->eflags, &pdb_out_buffer[idx], sizeof(regs->eflags)); + idx += sizeof(regs->eflags) * 2; + mem2hex ((char *)®s->xcs, &pdb_out_buffer[idx], sizeof(regs->xcs)); + idx += sizeof(regs->xcs) * 2; + mem2hex ((char *)®s->xss, &pdb_out_buffer[idx], sizeof(regs->xss)); + idx += sizeof(regs->xss) * 2; + mem2hex ((char *)®s->xds, &pdb_out_buffer[idx], sizeof(regs->xds)); + idx += sizeof(regs->xds) * 2; + mem2hex ((char *)®s->xes, &pdb_out_buffer[idx], sizeof(regs->xes)); + idx += sizeof(regs->xes) * 2; + mem2hex ((char *)®s->xfs, &pdb_out_buffer[idx], sizeof(regs->xfs)); + idx += sizeof(regs->xfs) * 2; + mem2hex ((char *)®s->xgs, &pdb_out_buffer[idx], sizeof(regs->xgs)); + + /* + TRC(printk (" reg: %s \n", pdb_out_buffer)); + TRC(printk (" ebx: 0x%08lx\n", regs->ebx)); + TRC(printk (" ecx: 0x%08lx\n", regs->ecx)); + TRC(printk (" edx: 0x%08lx\n", regs->edx)); + TRC(printk (" esi: 0x%08lx\n", regs->esi)); + TRC(printk (" edi: 0x%08lx\n", regs->edi)); + TRC(printk (" ebp: 0x%08lx\n", regs->ebp)); + TRC(printk (" eax: 0x%08lx\n", regs->eax)); + TRC(printk (" xds: 0x%08x\n", regs->xds)); + TRC(printk (" xes: 0x%08x\n", regs->xes)); + TRC(printk (" xfs: 0x%08x\n", regs->xfs)); + TRC(printk (" xgs: 0x%08x\n", regs->xgs)); + TRC(printk (" eip: 0x%08lx\n", regs->eip)); + TRC(printk (" xcs: 0x%08x\n", regs->xcs)); + TRC(printk (" efl: 0x%08lx\n", regs->eflags)); + TRC(printk (" esp: 0x%08lx\n", regs->esp)); + TRC(printk (" xss: 0x%08x\n", regs->xss)); + */ + + break; + } + case 'G': /* set the value of the CPU registers - return OK */ + break; + + case 'H': + { + int thread; + char *next = &ptr[1]; + if (hexToInt (&next, &thread)) + { + if (thread > 0) + { + thread = thread - PDB_DOMAIN_OFFSET; + } + if (*ptr == 'c') + { + pdb_ctrl_thread = thread; + } + else if (*ptr == 'g') + { + pdb_info_thread = thread; + } + else + { + printk ("ack, unknown command %c (thread: %d)\n", + *ptr, thread); + } + } + strcpy (pdb_out_buffer, "OK"); + break; + } + case 'k': /* kill request */ + { + strcpy (pdb_out_buffer, "OK"); /* ack for fun */ + printk ("don't kill bill...\n"); + ack = 0; + break; + } + + case 'q': + { + pdb_process_query(ptr); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + { + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + if (hexToInt (&ptr, (int *)&addr)) + if (*(ptr++) == ',') + if (hexToInt (&ptr, &length)) + { + ptr = 0; + mem_err = 0; + + if (pdb_info_thread >= 0) + { + pdb_get_values(pdb_info_thread, pdb_buffer, addr, length); + mem2hex (pdb_buffer, pdb_out_buffer, length); + } + else + mem2hex ((char *) addr, pdb_out_buffer, length); + if (mem_err) + { + strcpy (pdb_out_buffer, "E03"); + } + } + + if (ptr) + { + strcpy (pdb_out_buffer, "E01"); + } + break; + } + + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + case 'M': + { + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + if (hexToInt (&ptr, (int *)&addr)) + if (*(ptr++) == ',') + if (hexToInt (&ptr, &length)) + if (*(ptr++) == ':') + { + mem_err = 0; + + pdb_set_values(pdb_info_thread, + ptr, addr, length); + + if (mem_err) + { + strcpy (pdb_out_buffer, "E03"); + } + else + { + strcpy (pdb_out_buffer, "OK"); + } + + ptr = 0; + } + if (ptr) + { + strcpy (pdb_out_buffer, "E02"); + } + break; + } + case 'T': + { + int thread; + if (hexToInt (&ptr, &thread)) + { + thread -= PDB_DOMAIN_OFFSET; + struct task_struct *p = find_domain_by_id(thread); + if (p == NULL) + { + strcpy (pdb_out_buffer, "E00"); + } + else + { + strcpy (pdb_out_buffer, "OK"); + } + put_task_struct(p); + } + break; + } + } /* switch */ + + /* reply to the request */ + pdb_put_packet (pdb_out_buffer, ack); + } + + return go; +} + +/* + * process an input character from the serial line. + * + * return "1" if the character is a gdb debug string + * (and hence shouldn't be further processed). + */ + +int pdb_debug_state = 0; /* small parser state machine */ + +int pdb_serial_input(u_char c, struct pt_regs *regs) +{ + int out = 1; + int loop, count; + + switch (pdb_debug_state) + { + case 0: /* not currently processing debug string */ + if ( c == '$' ) /* start token */ + { + pdb_debug_state = 1; + pdb_in_buffer_ptr = 0; + pdb_in_checksum = 0; + pdb_xmit_checksum = 0; + } + else + { + out = 0; + } + break; + case 1: /* saw '$' */ + if ( c == '#' ) /* checksum token */ + { + pdb_debug_state = 2; + pdb_in_buffer[pdb_in_buffer_ptr] = 0; + } + else + { + pdb_in_checksum += c; + pdb_in_buffer[pdb_in_buffer_ptr++] = c; + } + break; + case 2: /* 1st checksum digit */ + pdb_xmit_checksum = hex(c) << 4; + pdb_debug_state = 3; + break; + case 3: /* 2nd checksum digit */ + pdb_xmit_checksum += hex(c); + if (pdb_in_checksum != pdb_xmit_checksum) + { + pdb_put_char('-'); /* checksum failure */ + printk ("checksum failure [%s.%02x.%02x]\n", pdb_in_buffer, + pdb_in_checksum, pdb_xmit_checksum); + } + else + { + pdb_put_char('+'); /* checksum okay */ + if ( pdb_in_buffer_ptr > 1 && pdb_in_buffer[2] == ':' ) + { + pdb_put_char(pdb_in_buffer[0]); + pdb_put_char(pdb_in_buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(pdb_in_buffer); + for (loop = 3; loop < count; loop++) + pdb_in_buffer[loop - 3] = pdb_in_buffer[loop]; + } + + pdb_process_command (pdb_in_buffer, regs); + } + pdb_debug_state = 0; + break; + } + + return out; +} + +int hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10); + if ((ch >= '0') && (ch <= '9')) return (ch-'0'); + if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10); + return (-1); +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +char * +mem2hex (mem, buf, count) + char *mem; + char *buf; + int count; +{ + int i; + unsigned char ch; + + for (i = 0; i < count; i++) + { + ch = get_char (mem++); + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +char * +hex2mem (buf, mem, count) + char *buf; + char *mem; + int count; +{ + int i; + unsigned char ch; + + for (i = 0; i < count; i++) + { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + set_char (mem++, ch); + } + return (mem); +} + +int +hexToInt (char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + int negative = 0; + + *intValue = 0; + + if (**ptr == '-') + { + negative = 1; + numChars++; + (*ptr)++; + } + while (**ptr) + { + hexValue = hex (**ptr); + if (hexValue >= 0) + { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } + else + break; + + (*ptr)++; + } + if (negative) + { + *intValue *= -1; + } + + return (numChars); +} + +/***********************************************************************/ +/***********************************************************************/ + + +/* + * Add a breakpoint to the list of known breakpoints. + * For now there should only be two or three breakpoints so + * we use a simple linked list. In the future, maybe a red-black tree? + */ +struct pdb_breakpoint breakpoints; + + +void pdb_bkpt_add (unsigned long address) +{ + struct pdb_breakpoint *bkpt; + + bkpt = kmalloc(sizeof(struct pdb_breakpoint), GFP_KERNEL); + INIT_LIST_HEAD(&bkpt->list); + + bkpt->address = address; + + list_add(&bkpt->list, &breakpoints.list); + + return; +} + +/* + * Check to see of the breakpoint is in the list of known breakpoints + * + * return 1 if it has been set, 0 otherwise + */ + +struct pdb_breakpoint* pdb_bkpt_search (unsigned long address) +{ + struct pdb_breakpoint *found = NULL; + struct list_head *list_entry; + struct pdb_breakpoint *bkpt; + + list_for_each(list_entry, &breakpoints.list) + { + bkpt = list_entry(list_entry, struct pdb_breakpoint, list); + + if (bkpt->address == address) + { + found = bkpt; + break; + } + } + + return found; +} + +/* + * Remove a breakpoint to the list of known breakpoints. + * + * Return 1 if the element was not found, otherwise 0. + */ + +void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt) +{ + struct list_head *list_entry = &bkpt->list; + list_del(list_entry); + kfree(bkpt); +} + +int pdb_bkpt_remove (unsigned long address) +{ + struct list_head *list_entry; + struct pdb_breakpoint *bkpt; + int found = 1; + + list_for_each(list_entry, &breakpoints.list) + { + bkpt = list_entry(list_entry, struct pdb_breakpoint, list); + + if (bkpt->address == address) + { + pdb_bkpt_remove_ptr (bkpt); + found = 0; + break; + } + } + + return found; +} + +/***********************************************************************/ + +void breakpoint(void); + +int pdb_initialized = 0; +int pdb_high_bit = 1; + +void pdb_put_char (u_char c) +{ + extern void debug_putchar(u_char); + u_char cc = pdb_high_bit ? c | 0x80 : c; + debug_putchar(cc); +} + +u_char pdb_get_char () +{ + extern u_char debug_getchar(); + u_char cc = debug_getchar(); + return cc & 0x7f; +} + +/* send the packet in buffer. */ +void pdb_put_packet (unsigned char *buffer, int ack) +{ + unsigned char checksum; + int count; + char ch; + + /* $# */ + /* do */ + { + pdb_put_char ('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) + { + pdb_put_char (ch); + checksum += ch; + count += 1; + } + + pdb_put_char('#'); + pdb_put_char(hexchars[checksum >> 4]); + pdb_put_char(hexchars[checksum % 16]); + } + + if (ack) + { + if ((ch = pdb_get_char()) != '+') + { + printk(" pdb return error: %c 0x%x [%s]\n", ch, ch, buffer); + } + } +} + +void pdb_get_packet(char *buffer) +{ + int count; + char ch; + unsigned char checksum = 0; + unsigned char xmitcsum = 0; + + do + { + while ((ch = pdb_get_char()) != '$'); + + count = 0; + checksum = 0; + + while (count < BUFMAX) + { + ch = pdb_get_char(); + if (ch == '#') break; + checksum += ch; + buffer[count] = ch; + count++; + } + buffer[count] = 0; + + if (ch == '#') + { + xmitcsum = hex(pdb_get_char()) << 4; + xmitcsum += hex(pdb_get_char()); + + if (xmitcsum == checksum) + { + pdb_put_char('+'); + if (buffer[2] == ':') + { + printk ("gdb packet found with sequence ID\n"); + } + } + else + { + pdb_put_char('-'); + } + } + } while (checksum != xmitcsum); + + return; +} + +/* + * process a machine interrupt or exception + * return 1 if pdb is not interested in the exception; it should + * be propagated to the guest os. + */ + +int pdb_handle_exception(int exceptionVector, + struct pt_regs *xen_regs) +{ + int signal = 0; + + printk ("pdb_handle_exception [0x%x][0x%lx]\n", + exceptionVector, xen_regs->eip); + + /* if pdb didn't set the breakpoint, and + pdb is not single stepping, and + the user didn't press the magic debug key on the console, + then pass the exception up to the guest os */ + if (pdb_bkpt_search(xen_regs->eip - 1) == NULL && + pdb_stepping == 0 && + exceptionVector != 0x88) + { + TRC(printk("pdb: external breakpoint at 0x%lx\n", xen_regs->eip)); + return 1; + } + + if (pdb_stepping == 1) + { + xen_regs->eflags &= ~0x100; + pdb_stepping = 0; + } + + if (exceptionVector == 0x03) + { + xen_regs->eip --; + } + + /* generate a signal for gdb */ + switch (exceptionVector) + { + case 136 : signal = 2; break; /* SIGINT */ + case 1 : signal = 5; break; /* SIGTRAP */ + case 3 : signal = 5; break; /* SIGTRAP */ + default : + printk ("can't generate signal for unknown exception vector %d\n", + exceptionVector); + break; + } + + pdb_out_buffer[0] = 'S'; + pdb_out_buffer[1] = hexchars[signal >> 4]; + pdb_out_buffer[2] = hexchars[signal % 16]; + pdb_out_buffer[3] = 0; + pdb_put_packet(pdb_out_buffer, 1); + + while (1) + { + pdb_out_buffer[0] = 0; + pdb_get_packet(pdb_in_buffer); + if (pdb_process_command(pdb_in_buffer, xen_regs)) + { + return 0; + } + } + + return 0; +} + +void pdb_key_pressed(u_char key, void *dev_id, struct pt_regs *regs) +{ + pdb_handle_exception(136, regs); + return; +} + +void initialize_pdb() +{ + extern char opt_pdb[]; + int pdb_com_port; + + if (strncmp(opt_pdb, "com", 3) == 0) + { + extern void debug_set_com_port(int port); + + pdb_com_port = opt_pdb[3] - '0'; /* error checking ? */ + debug_set_com_port(pdb_com_port); + pdb_high_bit = opt_pdb[4] == 'H' ? 1 : 0; + } + else + { + if (strcmp(opt_pdb, "none") != 0) + { + printk ("pdb: unknown option\n"); + } + return; + } + + printk ("Initializing pervasive debugger (PDB) [%s] port %d, high %d\n", + opt_pdb, pdb_com_port, pdb_high_bit); + + breakpoints.address = 0; + INIT_LIST_HEAD(&breakpoints.list); + + pdb_stepping = 0; + + /* ack any spurrious gdb packets */ + pdb_put_char ('+'); + + /* serial console */ + add_key_handler('D', pdb_key_pressed, "enter pervasive debugger"); + + pdb_initialized = 1; +} + +void breakpoint(void) +{ + if (pdb_initialized) + asm("int $3"); +} diff --git a/xen/arch/i386/setup.c b/xen/arch/i386/setup.c index b0f6766b9f..ee52629dee 100644 --- a/xen/arch/i386/setup.c +++ b/xen/arch/i386/setup.c @@ -12,6 +12,7 @@ #include #include #include +#include struct cpuinfo_x86 boot_cpu_data = { 0 }; /* Lots of nice things, since we only target PPro+. */ @@ -428,6 +429,7 @@ void __init start_of_day(void) do_initcalls(); initialize_serial(); /* setup serial 'driver' (for debugging) */ initialize_keyboard(); /* setup keyboard (also for debugging) */ + initialize_pdb(); /* pervasive debugger */ if ( !setup_network_devices() ) panic("Must have a network device!\n"); diff --git a/xen/arch/i386/traps.c b/xen/arch/i386/traps.c index 78c26c37cc..4f2cd6af66 100644 --- a/xen/arch/i386/traps.c +++ b/xen/arch/i386/traps.c @@ -49,6 +49,7 @@ #include #include #include +#include #define GTBF_TRAP 1 #define GTBF_TRAP_NOCODE 2 @@ -223,6 +224,30 @@ static inline void do_trap(int trapnr, char *str, smp_processor_id(), trapnr, str, error_code); } +static inline void do_int3_exception(int trapnr, + struct pt_regs *regs, + long error_code) +{ + struct task_struct *p = current; + struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id(); + trap_info_t *ti; + + if ((regs->xcs & 3) != 3) + { + pdb_handle_exception(trapnr, regs); + return; + } + + ti = current->thread.traps + trapnr; + gtb->flags = GTBF_TRAP_NOCODE; + gtb->error_code = error_code; + gtb->cs = ti->cs; + gtb->eip = ti->address; + if ( TI_GET_IF(ti) ) + clear_bit(EVENTS_MASTER_ENABLE_BIT, &p->shared_info->events_mask); + return; +} + #define DO_ERROR_NOCODE(trapnr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ @@ -236,7 +261,6 @@ do_trap(trapnr, str, regs, error_code, 1); \ } DO_ERROR_NOCODE( 0, "divide error", divide_error) -DO_ERROR_NOCODE( 3, "int3", int3) DO_ERROR_NOCODE( 4, "overflow", overflow) DO_ERROR_NOCODE( 5, "bounds", bounds) DO_ERROR_NOCODE( 6, "invalid operand", invalid_op) @@ -251,6 +275,12 @@ DO_ERROR(17, "alignment check", alignment_check) DO_ERROR_NOCODE(18, "machine check", machine_check) DO_ERROR_NOCODE(19, "simd error", simd_coprocessor_error) +asmlinkage void do_int3(struct pt_regs * regs, long error_code) +{ + if (pdb_initialized) do_int3_exception(3, regs, error_code); + else do_trap(3, "int3", regs, error_code, 0); +} + asmlinkage void do_double_fault(void) { extern spinlock_t console_lock; @@ -489,13 +519,44 @@ asmlinkage void math_state_restore(struct pt_regs *regs, long error_code) } } - asmlinkage void do_debug(struct pt_regs * regs, long error_code) { unsigned int condition; struct task_struct *tsk = current; struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id(); + /* + printk("do_debug_exceptionn [%lx][%lx][%x]\n", + error_code, regs->eip, regs->xcs); + */ + + __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + + if ((condition & (1 << 14)) != (1 << 14)) + { + printk ("\nwarning: debug trap w/o BS bit [0x%x]\n\n", condition); + } + __asm__("movl %0,%%db6" : : "r" (0)); + + if (pdb_handle_exception(1, regs)) /* propagate to domain */ + { + tsk->thread.debugreg[6] = condition; + + gtb->flags = GTBF_TRAP_NOCODE; + gtb->cs = tsk->thread.traps[1].cs; + gtb->eip = tsk->thread.traps[1].address; + } + + return; +} + + +asmlinkage void do_debug_orig(struct pt_regs * regs, long error_code) +{ + unsigned int condition; + struct task_struct *tsk = current; + struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id(); + __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); /* Mask out spurious debug traps due to lazy DR7 setting */ diff --git a/xen/common/debug-linux.c b/xen/common/debug-linux.c new file mode 100644 index 0000000000..8e4f0c63dc --- /dev/null +++ b/xen/common/debug-linux.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +#include "debug-linux.h" + +/* + * linux specific pdb stuff + */ + +/* + static inline struct task_struct *find_task_by_pid(int pid) + { + struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)]; + + for(p = *htable; p && p->pid != pid; p = p->pidhash_next) ; + return p; + } +*/ + +/* read a byte from a process */ +u_char pdb_linux_get_value (int domain, int pid, unsigned long addr) +{ + u_char result = 0; + unsigned long task_struct_p, mm_p, pgd, task_struct_pid; + unsigned long l2tab, page; + + /* find the task_struct of the given process */ + pdb_get_values(domain, (u_char *) &task_struct_p, + pidhash_addr + pid_hashfn(pid) * 4, + sizeof(task_struct_p)); + + /* find the correct task struct */ + while (task_struct_p != (unsigned long)NULL) + { + pdb_get_values(domain, (u_char *) &task_struct_pid, + task_struct_p + task_struct_pid_offset, + sizeof(task_struct_pid)); + if (task_struct_pid == pid) + { + break; + } + + pdb_get_values(domain, (u_char *) &task_struct_p, + task_struct_p + task_struct_pidhash_next_offset, + sizeof(task_struct_p)); + } + if (task_struct_p == (unsigned long)NULL) + { + /* oops */ + printk ("error: couldn't find process 0x%x in domain %d\n", pid, domain); + return 0; + } + + /* get the mm_struct within the task_struct */ + pdb_get_values(domain, (u_char *) &mm_p, + task_struct_p + task_struct_mm_offset, + sizeof(mm_p)); + /* get the page global directory (cr3) within the mm_struct */ + pdb_get_values(domain, (u_char *) &pgd, + mm_p + mm_struct_pgd_offset, + sizeof(pgd)); + + /* get the l2 table entry */ + pdb_get_values(domain, (u_char *) &l2tab, + pgd + (addr >> PGDIR_SHIFT) * 4, + sizeof(l2tab)); + l2tab = (unsigned long)__va(machine_to_phys(domain, l2tab) & PAGE_MASK); + + /* get the page table entry */ + pdb_get_values(domain, (u_char *) &page, + l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4, + sizeof(page)); + page = (unsigned long)__va(machine_to_phys(domain, page) & PAGE_MASK); + + /* get the byte */ + pdb_get_values(domain, (u_char *) &result, page + (addr & ~PAGE_MASK), + sizeof(result)); + + return result; +} diff --git a/xen/common/debug-linux.h b/xen/common/debug-linux.h new file mode 100644 index 0000000000..ae93d34b26 --- /dev/null +++ b/xen/common/debug-linux.h @@ -0,0 +1,45 @@ +#include + +/* from linux/sched.h */ +#define PIDHASH_SZ (4096 >> 2) +#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) + +/* from asm-xeno/pgtable-2level.h */ +#define PGDIR_SHIFT 22 +#define PTRS_PER_PGD 1024 + +/* from asm-xeno/page.h */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#define __PAGE_OFFSET (0xC0000000) +#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) + +/* from debug.h */ +#define ENTRIES_PER_L1_PAGETABLE 1024 +#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << PAGE_SHIFT ) + + +/* adapted from asm-xeno/page.h */ +static inline unsigned long machine_to_phys(int domain, unsigned long machine) +{ + unsigned long phys; + pdb_get_values(domain, (u_char *) &phys, + (unsigned long) machine_to_phys_mapping + (machine >> PAGE_SHIFT) * 4, + sizeof(phys)); + phys = (phys << PAGE_SHIFT) | (machine & ~PAGE_MASK); + return phys; +} + + +#define pidhash_addr 0xc018f260UL + +#define task_struct_mm_offset 0x2c +#define task_struct_pid_offset 0x7c +#define task_struct_pidhash_next_offset 0xb0 +#define mm_struct_pgd_offset 0x0c + +extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr); diff --git a/xen/common/debug.c b/xen/common/debug.c index f233c4e1f7..852b3bca03 100644 --- a/xen/common/debug.c +++ b/xen/common/debug.c @@ -10,33 +10,182 @@ #include #include #include +#include +#include /* [un]map_domain_mem */ +#include -#define DEBUG_TRACE +#undef DEBUG_TRACE #ifdef DEBUG_TRACE #define TRC(_x) _x #else #define TRC(_x) #endif +/****************************************************************************/ + +int pdb_change_values (int domain, u_char *buffer, unsigned long addr, + int length, int rw); + +/* + * Set memory in a domain's address space + * Set "length" bytes at "address" from "domain" to the values in "buffer". + * Return the number of bytes set, 0 if there was a problem. + * + * THIS WILL BECOME A MACRO + */ + +int pdb_set_values (int domain, u_char *buffer, unsigned long addr, int length) +{ + int count; + void *bkpt; + count = pdb_change_values(domain, buffer, addr, length, 2); + + /* this is a bit x86 specific at the moment... */ + if (length == 1 && buffer[0] == 'c' && buffer[1] == 'c') + { + /* inserting a new breakpoint */ + pdb_bkpt_add (addr); + TRC(printk("pdb breakpoint detected at 0x%lx\n", addr)); + } + else if ((bkpt = pdb_bkpt_search(addr))) + { + /* removing a breakpoint */ + TRC(printk("pdb breakpoint cleared at 0x%lx\n", addr)); + pdb_bkpt_remove_ptr(bkpt); + } + + return count; +} + +/* + * Read memory from a domain's address space. + * Fetch "length" bytes at "address" from "domain" into "buffer". + * Return the number of bytes read, 0 if there was a problem. + * + * THIS WILL BECOME A MACRO + */ + +int pdb_get_values (int domain, u_char *buffer, unsigned long addr, int length) +{ + return pdb_change_values(domain, buffer, addr, length, 1); +} + +/* + * Change memory in a domain's address space. + * Read or write "length" bytes at "address" from "domain" into/from "buffer". + * Return the number of bytes read, 0 if there was a problem. + * RW: 1 = read, 2 = write + */ + +int pdb_change_values (int domain, u_char *buffer, unsigned long addr, + int length, int rw) +{ + struct task_struct *p; + l2_pgentry_t* l2_table = NULL; + l1_pgentry_t* l1_table = NULL; + u_char *page; + int bytes = 0; + + extern char *hex2mem (char *, char *, int); + + p = find_domain_by_id(domain); + + if ((addr >> PAGE_SHIFT) == ((addr + length - 1) >> PAGE_SHIFT)) + { + l2_table = map_domain_mem(pagetable_val(p->mm.pagetable)); + l2_table += l2_table_offset(addr); + if (!(l2_pgentry_val(*l2_table) & _PAGE_PRESENT)) + { + printk ("L2:0x%p (0x%lx) \n", l2_table, l2_pgentry_val(*l2_table)); + goto exit2; + } + + if (l2_pgentry_val(*l2_table) & _PAGE_PSE) + { +#define PSE_PAGE_SHIFT L2_PAGETABLE_SHIFT +#define PSE_PAGE_SIZE (1UL << PSE_PAGE_SHIFT) +#define PSE_PAGE_MASK (~(PSE_PAGE_SIZE-1)) + +#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << L1_PAGETABLE_SHIFT ) + +#define pse_pgentry_to_phys(_x) (l2_pgentry_val(_x) & PSE_PAGE_MASK) + + page = map_domain_mem(pse_pgentry_to_phys(*l2_table) +/* 10 bits */ + (addr & L1_PAGE_BITS)); /* 10 bits */ + page += addr & (PAGE_SIZE - 1); /* 12 bits */ + } + else + { + l1_table = map_domain_mem(l2_pgentry_to_phys(*l2_table)); + l1_table += l1_table_offset(addr); + if (!(l1_pgentry_val(*l1_table) & _PAGE_PRESENT)) + { + printk ("L2:0x%p (0x%lx) L1:0x%p (0x%lx)\n", + l2_table, l2_pgentry_val(*l2_table), + l1_table, l1_pgentry_val(*l1_table)); + goto exit1; + } + + page = map_domain_mem(l1_pgentry_to_phys(*l1_table)); + page += addr & (PAGE_SIZE - 1); + } + + switch (rw) + { + case 1: /* read */ + memcpy (buffer, page, length); + bytes = length; + break; + case 2: /* write */ + hex2mem (buffer, page, length); + bytes = length; + break; + default: /* unknown */ + printk ("error: unknown RW flag: %d\n", rw); + return 0; + } + + unmap_domain_mem((void *)page); + exit1: + if (l1_table != NULL) + unmap_domain_mem((void *)l1_table); + exit2: + unmap_domain_mem((void *)l2_table); + } + else + { + /* read spans pages. need to recurse */ + printk ("pdb memory SPAN! addr:0x%lx l: %x\n", addr, length); + } + + put_task_struct(p); + return bytes; +} + + +/* + * interactively call pervasive debugger from a privileged domain + */ void pdb_do_debug (dom0_op_t *op) { op->u.debug.status = 0; - op->u.debug.out1 = op->u.debug.in2 + 10; - op->u.debug.out2 = op->u.debug.in1 + 100; - TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x\n", + TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x, in3:%x, in4:%x\n", op->u.debug.opcode, op->u.debug.domain, - op->u.debug.in1, op->u.debug.in2)); + op->u.debug.in1, op->u.debug.in2, + op->u.debug.in3, op->u.debug.in4)); + /* NOT NOW if (op->u.debug.domain == 0) { op->u.debug.status = 1; return; } + */ switch (op->u.debug.opcode) { - case 'r' : + case 'c' : { struct task_struct * p = find_domain_by_id(op->u.debug.domain); if ( p != NULL ) @@ -54,6 +203,28 @@ void pdb_do_debug (dom0_op_t *op) } break; } + case 'r' : + { + int loop; + u_char x; + + for (loop = 0; loop < op->u.debug.in2; loop++) /* length */ + { + extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr); + + if (loop % 8 == 0) + { + printk ("\n%08x ", op->u.debug.in1 + loop); + } + x = pdb_linux_get_value(op->u.debug.domain, /* domain */ + op->u.debug.in3, /* pid */ + op->u.debug.in1 + loop); /* addr */ + printk (" %02x", x); + } + printk ("\n"); + break; + } + case 's' : { unsigned long cpu_mask; diff --git a/xen/common/kernel.c b/xen/common/kernel.c index 1bba43d7be..86138c59e6 100644 --- a/xen/common/kernel.c +++ b/xen/common/kernel.c @@ -69,6 +69,8 @@ int opt_noreboot=0; int opt_ignorebiostables=0; /* opt_watchdog: If true, run a watchdog NMI on each processor. */ int opt_watchdog=0; +/* opt_pdb: Name of serial port for Xen pervasive debugger (and enable pdb) */ +unsigned char opt_pdb[10] = "none"; static struct { unsigned char *name; @@ -85,6 +87,7 @@ static struct { { "noreboot", OPT_BOOL, &opt_noreboot }, { "ignorebiostables", OPT_BOOL, &opt_ignorebiostables }, { "watchdog", OPT_BOOL, &opt_watchdog }, + { "pdb", OPT_STR, &opt_pdb }, { NULL, 0, NULL } }; diff --git a/xen/drivers/char/xen_serial.c b/xen/drivers/char/xen_serial.c index c6457bf998..582cccbc75 100644 --- a/xen/drivers/char/xen_serial.c +++ b/xen/drivers/char/xen_serial.c @@ -41,31 +41,114 @@ #define NS16550_MCR_OUT2 0x08 /* OUT2: interrupt mask */ #define NS16550_MCR_LOOP 0x10 /* Loop */ -#define SERIAL_BASE 0x3f8 /* XXX SMH: horrible hardwired COM1 */ +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ -static int serial_echo = 0; /* default is not to echo; change with 'e' */ +#define SERIAL_COM1 0x3f8 +#define SERIAL_COM2 0x2f8 +int serial_com_base = SERIAL_COM1; +int debug_com_base = SERIAL_COM1; + + +static int serial_echo = 0; /* default is not to echo; change with '~' */ void toggle_echo(u_char key, void *dev_id, struct pt_regs *regs) { serial_echo = !serial_echo; } +void debug_set_com_port(int port) +{ + debug_com_base = port == 1 ? SERIAL_COM1 : SERIAL_COM2; +} + +int debug_testchar() /* character available? */ +{ + return (inb(debug_com_base + NS16550_LSR) & LSR_DR); +} + +u_char debug_getchar() +{ + while (! (inb(debug_com_base + NS16550_LSR) & LSR_DR));/* wait for char */ + return inb(debug_com_base + NS16550_RBR); +} + +void debug_putch(u_char c) +{ + while (! (inb(debug_com_base + NS16550_LSR) & LSR_THRE)); + /* wait for idle */ + outb(c, debug_com_base + NS16550_RBR); +} + +void debug_putchar(u_char c) +{ + debug_putch(c); + if (c == '\n') debug_putch('\r'); +} + + + +int serial_testchar() /* character available? */ +{ + return (inb(serial_com_base + NS16550_LSR) & LSR_DR); +} + +u_char serial_getchar() +{ + while (! (inb(serial_com_base + NS16550_LSR) & LSR_DR));/* wait for char */ + return inb(serial_com_base + NS16550_RBR); +} + +void serial_putch(u_char c) +{ + while (! (inb(serial_com_base + NS16550_LSR) & LSR_THRE)); + /* wait for idle */ + outb(c, serial_com_base + NS16550_RBR); +} + +void serial_putchar(u_char c) +{ + serial_putch(c); + if (c == '\n') serial_putch('\r'); +} + +static spinlock_t serial_lock; + static void serial_rx_int(int irq, void *dev_id, struct pt_regs *regs) { u_char c; key_handler *handler; + unsigned long flags; - while ( (inb(SERIAL_BASE + NS16550_LSR) & 1) == 1 ) - { - c = inb(SERIAL_BASE + NS16550_RBR); - - if( (handler = get_key_handler(c)) != NULL ) - (*handler)(c, dev_id, regs); + spin_lock_irqsave(&serial_lock, flags); - if ( serial_echo ) - printk("%c", c); - } + while (serial_testchar()) + { + c = serial_getchar(); + + if (c & 0x80) + { + extern int pdb_serial_input(u_char, struct pt_regs *); + pdb_serial_input(c & 0x7f, regs); + } + else + { + if ( (handler = get_key_handler(c)) != NULL ) + (*handler)(c, dev_id, regs); + + if ( serial_echo ) + serial_putch(c); + } + } + + spin_unlock_irqrestore(&serial_lock, flags); } void initialize_serial() @@ -74,6 +157,8 @@ void initialize_serial() if ( !SERIAL_ENABLED ) return; + + spin_lock_init(&serial_lock); /* setup key handler */ add_key_handler('~', toggle_echo, "toggle serial echo"); @@ -82,13 +167,13 @@ void initialize_serial() /* Clear FIFOs, enable, trigger at 1 byte */ outb(NS16550_FCR_TRG1 | NS16550_FCR_ENABLE | NS16550_FCR_CLRX | NS16550_FCR_CLTX, - SERIAL_BASE+NS16550_FCR); + serial_com_base + NS16550_FCR); /* Enable receive interrupts. Also remember to keep DTR/RTS asserted. */ outb(NS16550_MCR_OUT2|NS16550_MCR_DTR|NS16550_MCR_RTS, - SERIAL_BASE + NS16550_MCR); + serial_com_base + NS16550_MCR); outb(NS16550_IER_ERDAI, - SERIAL_BASE + NS16550_IER ); + serial_com_base + NS16550_IER ); if( (rc = request_irq(4, serial_rx_int, SA_NOPROFILE, "serial", 0)) ) printk("initialize_serial: failed to get IRQ4, rc=%d\n", rc); diff --git a/xen/include/asm-i386/pdb.h b/xen/include/asm-i386/pdb.h new file mode 100644 index 0000000000..5799328ea1 --- /dev/null +++ b/xen/include/asm-i386/pdb.h @@ -0,0 +1,41 @@ + +/* + * pervasive debugger + * + * alex ho + * 2004 + * university of cambridge computer laboratory + */ + + +#ifndef __PDB_H__ +#define __PDB_H__ + +#include +#include + +extern int pdb_initialized; +extern int pdb_com_port; +extern int pdb_high_bit; + +extern void initialize_pdb(void); +extern int pdb_set_values (int domain, u_char *buffer, + unsigned long addr, int length); +extern int pdb_get_values (int domain, u_char *buffer, + unsigned long addr, int length); + +extern int pdb_handle_exception(int exceptionVector, + struct pt_regs *xen_regs); + + +struct pdb_breakpoint +{ + struct list_head list; + unsigned long address; +}; +extern void pdb_bkpt_add (unsigned long address); +extern struct pdb_breakpoint* pdb_bkpt_search (unsigned long address); +extern void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt); +extern int pdb_bkpt_remove (unsigned long address); + +#endif /* __PDB_H__ */ diff --git a/xen/include/hypervisor-ifs/dom0_ops.h b/xen/include/hypervisor-ifs/dom0_ops.h index e1d20ff2c7..e260c1c033 100644 --- a/xen/include/hypervisor-ifs/dom0_ops.h +++ b/xen/include/hypervisor-ifs/dom0_ops.h @@ -17,7 +17,7 @@ * This makes sure that old versions of dom0 tools will stop working in a * well-defined way (rather than crashing the machine, for instance). */ -#define DOM0_INTERFACE_VERSION 0xAAAA0003 +#define DOM0_INTERFACE_VERSION 0xAAAA0004 /* @@ -168,9 +168,11 @@ typedef struct dom0_debug_st { /* IN variables. */ char opcode; - int domain, in1, in2; + unsigned int domain; + int in1, in2, in3, in4; /* OUT variables. */ - int status, out1, out2; + unsigned int status; + int out1, out2; } dom0_debug_t; /* diff --git a/xen/include/xeno/sched.h b/xen/include/xeno/sched.h index 2e9cd0c563..9ac28fa8ed 100644 --- a/xen/include/xeno/sched.h +++ b/xen/include/xeno/sched.h @@ -159,7 +159,7 @@ struct task_struct * or expiring timer * TASK_UNINTERRUPTIBLE: Domain is blocked but may not be woken up by an * arbitrary event or timer. - * TASK_STOPPED: Domain is sopped. + * TASK_STOPPED: Domain is stopped. * TASK_DYING: Domain is about to cross over to the land of the dead. */ -- 2.30.2